
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>MyBatis学习 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>MyBatis学习 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">MyBatis被称为“半ORM”框架，这与像Hibernate这样的“全自动”ORM框架相比，最核心的区别是什么？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">核心区别在于对SQL的控制权。MyBatis是半自动的，要求开发者自己编写SQL语句，这为优化性能提供了更大的灵活性。而Hibernate是全自动的ORM框架，它允许开发者通过对象和方法来操作数据库，而不需要直接编写SQL语句。</div>
          </div>
          <div class="card-source">来源: 常见的ORM框架</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">MyBatis的动态SQL特性主要解决了什么问题，并通过哪些常用标签来实现？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">动态SQL主要用于根据不同的条件动态构建SQL语句，解决了处理复杂条件查询时的灵活性问题，同时提高了SQL的重用性和可维护性。常用的动态标签有 <if>、<choose>、<when>、<otherwise>、<foreach> 等。</div>
          </div>
          <div class="card-source">来源: MyBatis的优势和适用场景, MyBaits动态标签</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">当数据库表的列名与Java实体类的属性名不一致时，MyBatis的自动ORM映射会失效。文档中提到了哪两种解决方案？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">文档中提到了两种解决方案：1. 在SQL查询语句中使用 'as' 关键字为字段设置列别名，使其与实体属性名匹配。2. 使用 <ResultMap> 标签来明确定义数据库查询结果集与Java对象之间的映射关系。</div>
          </div>
          <div class="card-source">来源: ORM映射</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在MyBatis中，如何处理一对多（如一个用户拥有多个订单）的级联关系映射？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">在处理一对多关系时，应在 <resultMap> 标签内使用 <collection> 标签。<collection> 标签用于映射集合类型的属性（如 List<Order>），其中 'property' 属性指定实体类中的集合属性名，'ofType' 属性指定集合中元素的类型。</div>
          </div>
          <div class="card-source">来源: MyBatis级联操作</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">请简述MyBatis一级缓存和二级缓存的主要区别。</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">主要区别在于作用域和共享性。一级缓存是SqlSession级别的，默认开启，只在同一个SqlSession中有效，数据不被多个SqlSession共享。二级缓存是mapper（或namespace）级别的，需要手动配置开启，其数据可以被多个SqlSession共享。</div>
          </div>
          <div class="card-source">来源: MyBatis缓存</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在MyBatis中启用并配置二级缓存需要哪两个关键步骤？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">需要两步配置：1. 在核心配置文件 mybatis-config.xml 的 <settings> 中设置 <setting name="cacheEnabled" value="true"/> 来全局启用。2. 在需要开启缓存的Mapper XML文件中添加 <cache/> 标签。</div>
          </div>
          <div class="card-source">来源: 二级缓存</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">在MyBatis的SQL映射中，'#{ }' 和 '${ }' 两种参数占位符在预编译处理和安全性方面有何核心区别？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">核心区别在于：'#{ }' 是预编译处理的占位符，MyBatis会将其替换为 '?' 并使用 PreparedStatement 设置参数，能有效防止SQL注入攻击。而 '${ }' 是直接的字符串替换，直接将参数值拼接到SQL语句中，如果参数未经过滤，可能导致SQL注入风险。</div>
          </div>
          <div class="card-source">来源: MyBatis两个符号</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在执行INSERT操作后，如果需要获取由数据库自动生成的主键值，应如何在Mapper XML文件中配置？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">可以在 <insert> 标签中配置两个属性来实现：设置 `useGeneratedKeys="true"` 来启用主键回填功能，并使用 `keyProperty` 属性指定要回填到Java对象中的哪个属性，例如 `keyProperty="id"`。</div>
          </div>
          <div class="card-source">来源: MyBatis主键回填</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">在封装MyBatis工具类时，对于`SqlSessionFactory`和`SqlSession`对象的生命周期管理，文档推荐了什么最佳实践？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`SqlSessionFactory`是重量级对象，耗费资源，建议每个应用只创建一个。`SqlSession`相当于数据库连接，是线程不安全的，应该设为线程私有（例如使用ThreadLocal），不被多线程共享。</div>
          </div>
          <div class="card-source">来源: MyBatis工具类封装</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">MyBatis中的别名（typeAliases）设置主要起到了什么作用？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">别名的主要作用是简化映射文件的配置。它允许开发者在映射文件中使用简短的别名来代替长且复杂的全限定类名，从而提高配置文件的可读性和可维护性。</div>
          </div>
          <div class="card-source">来源: 别名设置</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">当Mapper接口方法需要传递多个参数时，除了将它们封装成一个对象外，还可以使用哪种方式为参数指定名称以便在XML中引用？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">可以使用`@Param`注解为方法中的每个参数指定一个别名。在Mapper XML文件中，就可以通过这个别名来引用对应的参数值，例如`public User findUser4(@Param("uname") String username, ...)`，在XML中就可以使用`#{uname}`来获取username的值。</div>
          </div>
          <div class="card-source">来源: MyBatis参数传递</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">MyBatis的一级缓存（SqlSession级别缓存）在哪些情况下会失效或被清除？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">一级缓存是基于SqlSession的，当SqlSession关闭或者执行了清空缓存的操作时，一级缓存中的数据就会被清除。此外，任何增、删、改操作的执行也会导致一级缓存被清空，以保证缓存数据的准确性。</div>
          </div>
          <div class="card-source">来源: 一级缓存</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
